package gui;

import gui.popup.Popup;
import gui.table.AutoTable_DBList;
import gui.table.ListTransformer;
import gui.table.TableView;

import java.sql.SQLException;

import models.DatabaseModel;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ExpandBar;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;

import databasetranslation.DatabaseList;

/**
 * A tab with a toolbar, a table and an infobar.
 * The toolbar and the infobar must be filled by subclasses.
 *
 */
public abstract class Tab < Model extends DatabaseModel, 
							ModelList extends DatabaseList<Model>,
							ModelPopup extends Popup<Model> > {
	
	private App app;
	private TabItem tabWidget;
	private Composite panel;
	private AutoTable_DBList<Model, ModelList> table;
	private ExpandBar infoBar;
	private ListTransformer<Model> transformer;
	private ModelPopup popup;

	/**
	 * Create a new tab.
	 * 
	 * @param tabFolder The <code>TabFolder</code> which this Tab is part of.
	 * @param tabHeader The title to be displayed in the tab "button"
	 * @param databaseList list to display in the table
	 * @param tableView An object that implements <code>TableView</code> (used to fill the table with data)
	 * @param popup popup to use for editing list items.
	 */
	public Tab(App app, TabFolder tabFolder, String tabHeader, ModelList databaseList, TableView<Model> tableView, ModelPopup popup) {
		this.app = app;
		this.popup = popup;
		this.transformer = new ListTransformer<Model>();
		
		build(tabFolder, tabHeader, tableView);
		this.table.setList(databaseList);
	}
	
	private void build(TabFolder tabFolder, String tabHeader, TableView<Model> tableView) {
		this.tabWidget = new TabItem(tabFolder, SWT.NONE);
		this.tabWidget.setText(tabHeader);
		this.panel = new Composite(tabWidget.getParent(), SWT.NONE);
		this.tabWidget.setControl(panel);
		
		panel.setLayout(new GridLayout(2, false));
		Composite toolBar = new Composite(panel, SWT.NONE);
		GridData toolbarLayoutData = new GridData();
		toolbarLayoutData.grabExcessHorizontalSpace = true;
		toolbarLayoutData.horizontalAlignment = GridData.FILL;
		toolBar.setLayoutData(toolbarLayoutData);
		
		infoBar = new ExpandBar (panel, SWT.V_SCROLL);
		GridData layoutData = new GridData();
		layoutData.widthHint=250;
		layoutData.verticalSpan = 2;
		layoutData.verticalAlignment = GridData.FILL;
		infoBar.setLayoutData(layoutData);
		infoBar.setSpacing(8);

		table = new AutoTable_DBList<Model, ModelList>(getApp(), panel, tableView, transformer);
		GridData tableLayoutData = new GridData();
		tableLayoutData.grabExcessVerticalSpace = true;
		tableLayoutData.verticalAlignment = GridData.FILL;
		tableLayoutData.grabExcessHorizontalSpace = true;
		tableLayoutData.horizontalAlignment = GridData.FILL;
		table.setLayoutData(tableLayoutData);

		buildToolbar(toolBar);		
		buildInfobar(infoBar);
	}
	
	/**
	 * Fills the Composite toolbar with GUI elements.
	 * 
	 * @param toolBar Composite to be filled.
	 */
	protected abstract void buildToolbar(Composite toolBar); 
	
	/**
	 * Fills the ExpandBar infobar with GUI elements.
	 * 
	 * @param {@link org.eclipse.swt.widgets.ExpandBar ExpandBar} infoBar to be filled.
	 */
	protected abstract void buildInfobar(ExpandBar infoBar);
	
	/**
	 * Handy method for subsclasses for quickly adding search functionality.
	 *  
	 * @param toolBar
	 */
	protected void addSearchBar(Composite toolBar) {
		
		Label label = new Label(toolBar, SWT.None);
		label.setText("Zoeken:");

	    final Text searchText = new Text(toolBar, SWT.SEARCH);
	    GridData searchTextLayout = new GridData();
	    searchTextLayout.widthHint = 300;
	    searchText.setLayoutData(searchTextLayout);
	    searchText.addModifyListener(new ModifyListener(){
			@Override
			public void modifyText(ModifyEvent e) {
				String searchtext = searchText.getText();
				transformer.setSearch(searchtext);
				table.update();
			}
	    });
	    
		Button resetButton = new Button(toolBar, SWT.PUSH);
		resetButton.setText("Alles");
		resetButton.addListener(SWT.Selection, new Listener() {
			@Override
			public void handleEvent(Event event) {
				searchText.setText("");
				transformer.resetSearch();
				table.update();
			}
		});
		
		GridData resetButtonLayout = new GridData();
		resetButtonLayout.grabExcessHorizontalSpace = true;
		resetButton.setLayoutData(resetButtonLayout);
	}
	
	/**
	 * Handy method for subclasses for quickly adding standard 
	 * add, edit and delete methods.
	 * 
	 * @param toolBar
	 */
	protected void addManipulationButtons(Composite toolBar) {

		Button addButton = new Button(toolBar, SWT.PUSH);
		addButton.setText("Nieuw");
		addButton.setLayoutData(new GridData());
		addButton.addListener(SWT.Selection, new Listener(){
			@Override
			public void handleEvent(Event event) {
				addEntryEvent();
			}
		});
		
		Button changeButton = new Button(toolBar, SWT.PUSH);
		changeButton.setText("Wijzig");
		changeButton.setLayoutData(new GridData());
		changeButton.addListener(SWT.Selection, new Listener(){
			@Override
			public void handleEvent(Event event) {
				Model selected = table.getSelected();
				if (selected != null) {
					changeEntryEvent(selected);
				}
			}
		});
		
		Button deleteButton = new Button(toolBar, SWT.PUSH);
		deleteButton.setText("Verwijder");
		deleteButton.setLayoutData(new GridData());
		deleteButton.addListener(SWT.Selection, new Listener(){
			@Override
			public void handleEvent(Event event) {
				Model selected = table.getSelected();
				if (selected != null) {
					deleteEntryEvent(selected);
				}
			}
		});
		
	}
	
	/**
	 * Create an entry and add it to the list shown.
	 * @return True if successful
	 */
	private boolean addEntryEvent() {
		Model entry = popup.open_Add();
		if (entry != null) {
			try {
				getTable().add(entry);
				return true;
			} catch (SQLException e) {
				app.dbCommunicationException(e);
			}
		}
		return false;
	}
	
	/**
	 * Edit and entry in the list and show the new entry.
	 * @param oldEntry
	 * @return True if successful
	 */
	private boolean changeEntryEvent(Model entry) {
		Model newEntry = popup.open_Replace(entry);
		if (newEntry != null) {
			try {
				getTable().replace(entry);
				return true;
			} catch (SQLException e) {
				app.dbCommunicationException(e);
			}
		}
		return false;
	}
	
	/**
	 * Ask for confirmation to delete an entry, delete the entry and
	 * update the list shown.
	 * @param entry
	 * @return True if successful
	 */
	protected boolean deleteEntryEvent(Model entry) {
		boolean confirmation = popup.open_Delete(entry);
		if(confirmation){
			try {
				getTable().delete(entry);
				return true;
			} catch (SQLException e) {
				app.dbCommunicationException(e);
			}
		}
		return false;
	}	

	protected AutoTable_DBList<Model, ModelList> getTable() {
		return table;
	}
	
	/**
	 * Get the transformer that can transform the list displayed (filtering and searching).
	 * @return the transformer
	 */
	protected ListTransformer<Model> getListTransformer() {
		return transformer;
	}
	
	protected App getApp() {
		return app;
	}
	
	/**
	 * Update's the table of the current tab
	 */
	public void update() {
		table.update();
	}
	
}
